#include "dxut.h"
#include "dx11_mesh.h"
#include "SDKmisc.h"


CDX11Mesh::CDX11Mesh(void)
{
	this->m_pIndexBuffer = 0;
	this->m_pVertexBuffer = 0;
    m_bbMax = D3DXVECTOR3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
    m_bbMin = D3DXVECTOR3(FLT_MAX, FLT_MAX, FLT_MAX);
}

CDX11Mesh::~CDX11Mesh(void)
{
    OnDestroy();
}

void CDX11Mesh::CreateMeshFromOBJMemory( ID3D11Device* pd3dDevice, COBJLoader& objImport, 
		D3DXVECTOR3 scale /*= D3DXVECTOR3(1,1,1)*/, D3DXVECTOR3 rotation /*= D3DXVECTOR3(0, 0, 0)*/, D3DXVECTOR3 translation /*= D3DXVECTOR3(0, 0, 0)*/ )
{
	objImport.ScaleRotateTranslate(scale, rotation, translation);

	// sort triangles according to material and setup all subsets
	objImport.BuildMesh();
    
	OnDestroy();
	// get bounding box information
    m_bbMax = objImport.GetBBMax();
    m_bbMin = objImport.GetBBMin();

	///////////////////////////////////////////////////////////////////////////////////////////
	// get the subsets and material from objImport
    for(UINT i=0; i<objImport.GetSubsets().size(); i++)
    {
        Subset subset;
        subset.materialID = objImport.GetSubsets()[i].materialID;
        subset.startIndex = objImport.GetSubsets()[i].startIndex;
        subset.triangleCount = objImport.GetSubsets()[i].triangleCount;
        m_Subsets.push_back(subset);
    }

    for(UINT i=0; i<objImport.GetMaterials().size(); i++)
    {
        Material mat;
        mat.vAmbient = objImport.GetMaterials()[i].vAmbient;
        mat.vDiffuse = objImport.GetMaterials()[i].vDiffuse;
        mat.vSpecular = objImport.GetMaterials()[i].vSpecular;

        mat.fAlpha = objImport.GetMaterials()[i].fAlpha;
        mat.nShininess = objImport.GetMaterials()[i].nShininess;
        mat.bSpecular = objImport.GetMaterials()[i].bSpecular;

        wcscpy(mat.strTexture, objImport.GetMaterials()[i].strTexture);
        m_Materials.push_back(mat);
    }

	HRESULT hr;
	/////////////////////////////////////////////////////////////////////////////////////////////
	// Create vertex buffer from objImport
	D3D11_BUFFER_DESC bufferDesc;
	ZeroMemory(&bufferDesc, sizeof(bufferDesc));
	bufferDesc.BindFlags        = D3D11_BIND_VERTEX_BUFFER;
    bufferDesc.ByteWidth        = sizeof( Vertex ) * objImport.GetVertexBuffer().size(); //make sure vertex format
	bufferDesc.Usage            = D3D11_USAGE_IMMUTABLE;
	bufferDesc.CPUAccessFlags   = 0;
	bufferDesc.MiscFlags        = 0;

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Get vertex buffer from objLoader
    // do not directly cpy all memory from objloader 
    // instead use a tmp buffer to take in each vertex data
    // so that vertex definition in mesh here can be different from obj (etc texcoord may have different number of component)
    Vertex* vb_tmp = new Vertex[objImport.GetVertexBuffer().size()];
    for (unsigned int i = 0; i < objImport.GetVertexBuffer().size(); i++) 
    {
        vb_tmp[i].pos = objImport.GetVertexBuffer()[i].pos;
        vb_tmp[i].norm = objImport.GetVertexBuffer()[i].norm;
        vb_tmp[i].tex = objImport.GetVertexBuffer()[i].tex;
    }

	// Fill in the subresource data.
	D3D11_SUBRESOURCE_DATA InitData;
	ZeroMemory(&InitData, sizeof(InitData));
    InitData.pSysMem = vb_tmp;
	InitData.SysMemPitch = 0;
	InitData.SysMemSlicePitch = 0;

	// Create the vertex buffer.
	hr = pd3dDevice->CreateBuffer( &bufferDesc, &InitData, &(m_pVertexBuffer) );
	if( FAILED( hr ) )
		::MessageBoxA(0,"fail to create vb for obj", "d3dError", 0);
    
    SAFE_DELETE_ARRAY(vb_tmp);

    //////////////////////////////////////////////////////////////////////////////////////////
	// Create indices.
	// Fill in a buffer description.
	bufferDesc.Usage           = D3D11_USAGE_DEFAULT;
    bufferDesc.ByteWidth       = sizeof( unsigned int ) * objImport.GetIndexBuffer().size();
	bufferDesc.BindFlags       = D3D11_BIND_INDEX_BUFFER;
	bufferDesc.CPUAccessFlags  = 0;
	bufferDesc.MiscFlags       = 0;

	// Define the resource data.
	ZeroMemory(&InitData, sizeof(InitData));
    InitData.pSysMem = &(objImport.GetIndexBuffer()[0]); // directly grab indexbuffer from objloader
	InitData.SysMemPitch = 0;
	InitData.SysMemSlicePitch = 0;

	// Create the buffer with the device.
	hr = pd3dDevice->CreateBuffer( &bufferDesc, &InitData, &(this->m_pIndexBuffer) );
	if( FAILED( hr ) )
		::MessageBoxA(0,"fail to create vb for obj", "d3dError", 0);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// set directory for loading textures
    WCHAR wstrOldDir[FILENAME_MAX] = {0};
	GetCurrentDirectory( FILENAME_MAX, wstrOldDir );
    SetCurrentDirectory( objImport.GetMediaDirectory());

	// load textures required in .mtl file
	for ( UINT iMaterial = 0; iMaterial < m_Materials.size(); ++iMaterial )
	{
		Material *pMaterial = &m_Materials[ iMaterial ];

		if ( pMaterial->strTexture[0] )
		{    
			// Avoid loading the same texture twice
			bool bFound = false;

			// loop through previous materials to see if texture is already loaded.
			for( UINT x = 0; x < iMaterial; x++ )
			{
				Material* pCur = &m_Materials[x];
				if( 0 == wcscmp( pCur->strTexture, pMaterial->strTexture ) )
				{
					pMaterial->pTextureRV11 = pCur->pTextureRV11;
					bFound = true;
					break;
				}
			}

			// Not found, load the texture
			if(!bFound)
			{
				WCHAR wstr[ FILENAME_MAX ] = {0};
				if ( SUCCEEDED(DXUTFindDXSDKMediaFileCch( wstr, FILENAME_MAX, pMaterial->strTexture) ) )
				{
					D3DX11_IMAGE_INFO srcInfo;
					D3DX11GetImageInfoFromFile(wstr, NULL, &srcInfo, NULL);

					D3DX11_IMAGE_LOAD_INFO loadInfo;
					loadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE ;
					loadInfo.CpuAccessFlags = 0;
					loadInfo.MiscFlags = 0;
					loadInfo.FirstMipLevel = 0;
					loadInfo.MipLevels = srcInfo.MipLevels;
					loadInfo.pSrcInfo = &srcInfo;
					loadInfo.Format = srcInfo.Format;
					loadInfo.Width = srcInfo.Width;
					loadInfo.Height = srcInfo.Height;
					loadInfo.Depth = srcInfo.Depth;
					loadInfo.Filter = D3DX11_FILTER_NONE  ;               

					hr = D3DX11CreateShaderResourceViewFromFile( pd3dDevice, wstr, &loadInfo, NULL, &pMaterial->pTextureRV11, NULL );
					if(FAILED(hr)) ::MessageBoxW(0, L"fail to load material texture", L"D3D11 Error", 0);

				}
			}
		}
	}

	// Restore the original current directory
	SetCurrentDirectory( wstrOldDir );
}

void CDX11Mesh::CreateMeshFromOBJFile(ID3D11Device* pd3dDevice, const WCHAR* strFilename, 
								D3DXVECTOR3 scale, D3DXVECTOR3 rotation, D3DXVECTOR3 translation)
{
	COBJLoader objImport;
	objImport.LoadModelFromOBJ(strFilename);
	CreateMeshFromOBJMemory(pd3dDevice, objImport, scale, rotation, translation);
}

void CDX11Mesh::OnDestroy()
{
	SAFE_RELEASE(m_pVertexBuffer);
	SAFE_RELEASE(m_pIndexBuffer);

	////////////////////////////////////////////////////////////////////////////
    // Release all the SRVs
    vector<ID3D11ShaderResourceView*> totalSRVs;
    for (UINT i = 0; i < m_Materials.size(); i++)
    {
        if (m_Materials[i].pTextureRV11 == 0)
            continue;

        bool bFound = false;
        for (UINT v = 0; v < totalSRVs.size(); v++)
        {
            if (m_Materials[i].pTextureRV11 == totalSRVs[v])
                bFound = true;
        }
        if (!bFound)
            totalSRVs.push_back(m_Materials[i].pTextureRV11);
    }

    for (UINT i = 0; i < totalSRVs.size(); i++)
    {
        //ID3D11Resource* pRes = NULL;
        //totalSRVs[i]->GetResource(&pRes);
        //SAFE_RELEASE(pRes);
        //SAFE_RELEASE(pRes);
        SAFE_RELEASE(totalSRVs[i]);
    }

	std::vector<Subset>().swap(m_Subsets);			// all the sub meshes, each with different material property
	std::vector<Material>().swap(m_Materials);

    m_bbMax = D3DXVECTOR3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
    m_bbMin = D3DXVECTOR3(FLT_MAX, FLT_MAX, FLT_MAX);
}
